/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "MNTPDecoder.h"

#include "MAddressIP.h"

//---------------------------------------------------------------------------

MNTPDecoder::MNTPDecoder(void)
{
}

MNTPDecoder::~MNTPDecoder(void)
{
}

//---------------------------------------------------------------------------

bool MNTPDecoder::ComputeNtpTime(BYTE* bpIn)
{
	if(bpIn==NULL) return false;

	DWORD *iPtr;
	DWORD timeHi;
	DWORD timeLo;
	errno_t errt;
	struct tm tmx;
	double secp;

	iPtr = (DWORD*)&bpIn[0];
	timeHi = *iPtr;
	timeHi = ntohl(timeHi);
	if(timeHi==0) return false;
	timeHi -= (DWORD)2208988800;

	// 2208988800 = 2272060800 - 63072000
	// 2272060800 = NTP clock at 0h on 1 January 1972 (RFC 1305, paragraph E.8.)
	// 63072000 = 2 * 365 * 86400
	// 86400 = number of seconds in a day
	// 365 = number of days in 1970 and 1971

	iPtr = (DWORD*)&bpIn[4];
	timeLo = *iPtr;
	timeLo = ntohl(timeLo);
	if(timeLo==0) return false;

	errt = _gmtime32_s(&tmx, (__time32_t*)&timeHi);
	if(errt!=0) return false;

	secp = (double)timeLo / 4294967296.0;
	// 1.0000.0000(hexa) = 4294967296(decimal)

	tmx.tm_year += 1900;
	tmx.tm_mon  += 1;

	secp += (double)tmx.tm_sec;

	ntpTime.Format(_T("%04d-%02d-%02d %02d:%02d:%07.4f UTC"),
		tmx.tm_year,
		tmx.tm_mon,
		tmx.tm_mday,
		tmx.tm_hour,
		tmx.tm_min,
		secp);

	return true;
}

bool MNTPDecoder::ComputeNtpFloat32(BYTE* bpIn)
{
	if(bpIn==NULL) return false;

	WORD *iPtr;
	WORD valHi;
	WORD valLo;

	iPtr = (WORD*)&bpIn[0];
	valHi = *iPtr;
	valHi = ntohs(valHi);

	iPtr = (WORD*)&bpIn[2];
	valLo = *iPtr;
	valLo = ntohs(valLo);

	float32Val = (double)valHi + (double)valLo/65536.0;

	return true;
}

//---------------------------------------------------------------------------

CString MNTPDecoder::GetStratumName(NTPMessage *ntpMsgIn)
{
	CString    str, sss;
	char       cStr[5];
	in_addr    ia;
	MAddressIP aip;

	if(ntpMsgIn==NULL) return _T("");

	switch(ntpMsgIn->Stratum){
		case 0 :
		case 1 :
			str.Empty();
			ia.S_un.S_addr = ntpMsgIn->ReferenceIdentifier;
			cStr[0] = (char)ia.S_un.S_un_b.s_b1;
			cStr[1] = (char)ia.S_un.S_un_b.s_b2;
			cStr[2] = (char)ia.S_un.S_un_b.s_b3;
			cStr[3] = (char)ia.S_un.S_un_b.s_b4;
			cStr[4] = 0;

			sss = cStr;
			if(sss.Compare(_T("DCN"))==0)  sss = _T("DCN routing protocol");
			if(sss.Compare(_T("NIST"))==0) sss = _T("NIST public modem");
			if(sss.Compare(_T("TSP"))==0)  sss = _T("TSP time protocol");
			if(sss.Compare(_T("DTS"))==0)  sss = _T("Digital Time Service");

			if(sss.Compare(_T("ATOM"))==0) sss = _T("Atomic clock (calibrated)");
			if(sss.Compare(_T("VLF"))==0)  sss = _T("VLF radio (OMEGA, etc.)");
			if(sss.Compare(_T("LORC"))==0) sss = _T("LORAN-C radionavigation system");
			if(sss.Compare(_T("GOES"))==0) sss = _T("Geostationary Orbit Environment Satellite");
			if(sss.Compare(_T("GPS"))==0)  sss = _T("Global Positioning Service");

			if(sss.Compare(_T("LOCL"))==0) sss = _T("uncalibrated local clock used as a primary reference for a subnet without external means of synchronization");
			if(sss.Compare(_T("PPS"))==0)  sss = _T("atomic clock or other pulse-per-second source individually calibrated to national standards");
			if(sss.Compare(_T("ACTS"))==0) sss = _T("NIST dialup modem service");
			if(sss.Compare(_T("USNO"))==0) sss = _T("USNO modem service");
			if(sss.Compare(_T("PTB"))==0)  sss = _T("PTB (Germany) modem service");
			if(sss.Compare(_T("TDF"))==0)  sss = _T("Allouis (France) Radio 164 kHz");
			if(sss.Compare(_T("DCF"))==0)  sss = _T("Mainflingen (Germany) Radio 77.5 kHz");
			if(sss.Compare(_T("MSF"))==0)  sss = _T("Rugby (UK) Radio 60 kHz");
			if(sss.Compare(_T("WWV"))==0)  sss = _T("Ft. Collins (US) Radio 2.5, 5, 10, 15, 20 MHz");
			if(sss.Compare(_T("WWVB"))==0) sss = _T("Boulder (US) Radio 60 kHz");
			if(sss.Compare(_T("WWVH"))==0) sss = _T("Kaui Hawaii (US) Radio 2.5, 5, 10, 15 MHz");
			if(sss.Compare(_T("CHU"))==0)  sss = _T("Ottawa (Canada) Radio 3330, 7335, 14670 kHz");
			if(sss.Compare(_T("OMEG"))==0) sss = _T("OMEGA radionavigation system");

			if(ntpMsgIn->Stratum==0)
				str.Format(_T("Stratum 0: %s"), sss);
			else
				str.Format(_T("Stratum 1: %s"), sss);
			break;

		default:
			aip.Set(ntpMsgIn->ReferenceIdentifier);
			str.Format(_T("Primary reference host is at %s"), aip.GetS());
			break;
	}

	return str;
}

//---------------------------------------------------------------------------

CString MNTPDecoder::GetPollingInterval(NTPMessage *ntpMsgIn)
{
	CString str;
	DWORD   dwVal;
	BYTE    bVal;

	if(ntpMsgIn==NULL) return _T("");

	dwVal = 1;
	for(bVal=0; bVal<ntpMsgIn->Poll; bVal++)
		dwVal = dwVal<<1;

	str.Format(_T("%d"), dwVal);
	return str;
}

//---------------------------------------------------------------------------

CString MNTPDecoder::GetPrecision(NTPMessage *ntpMsgIn)
{
	CString str;
	DWORD   dwVal;
	BYTE    bVal;
	double  dVal;

	if(ntpMsgIn==NULL) return _T("");

	bVal = ntpMsgIn->Precision;
	if(bVal>0x7F){
		bVal = 0xFF - bVal;
		bVal++;
	}
	dwVal = 1;
	while(bVal>0){
		dwVal = dwVal<<1;
		bVal--;
	}
	bVal = ntpMsgIn->Precision;
	if(bVal>0x7F)
		dVal = 1.0 / (double)dwVal;
	else
		dVal = 1.0 * (double)dwVal;

	str.Format(_T("%f"), dVal);
	return str;
}

//---------------------------------------------------------------------------

CString MNTPDecoder::GetRootDelay(NTPMessage *ntpMsgIn)
{
	CString str;

	if(ComputeNtpFloat32((BYTE*)&(ntpMsgIn->RootDelay))){
		str.Format(_T("%01.4f"), float32Val);
		return str;
	}
	return _T("");
}

CString MNTPDecoder::GetRootDispersion(NTPMessage *ntpMsgIn)
{
	CString str;

	if(ComputeNtpFloat32((BYTE*)&(ntpMsgIn->RootDispersion))){
		str.Format(_T("%01.4f"), float32Val);
		return str;
	}
	return _T("");
}

//---------------------------------------------------------------------------

CString MNTPDecoder::GetReferenceTimestamp(NTPMessage *ntpMsgIn)
{
	if(ComputeNtpTime(ntpMsgIn->ReferenceTimestamp))
		return ntpTime;
	return _T("");
}

CString MNTPDecoder::GetOriginateTimestamp(NTPMessage *ntpMsgIn)
{
	if(ComputeNtpTime(ntpMsgIn->OriginateTimestamp))
		return ntpTime;
	return _T("");
}

CString MNTPDecoder::GetReceiveTimestamp(NTPMessage *ntpMsgIn)
{
	if(ComputeNtpTime(ntpMsgIn->ReceiveTimestamp))
		return ntpTime;
	return _T("");
}

CString MNTPDecoder::GetTransmitTimestamp(NTPMessage *ntpMsgIn)
{
	if(ComputeNtpTime(ntpMsgIn->TransmitTimestamp))
		return ntpTime;
	return _T("");
}

//---------------------------------------------------------------------------
